package com.wlyuan.index.domain.index.service;

import com.alibaba.fastjson2.JSON;
import com.wlyuan.core.exception.ValueError;
import com.wlyuan.index.api.dto.order.OrderIndexFilterDTO;
import com.wlyuan.index.api.dto.order.OrderIndexSearchDTO;
import com.wlyuan.index.api.vo.OrderIndexSumVO;
import com.wlyuan.index.api.vo.OrderIndexVO;
import com.wlyuan.index.domain.index.OrderIndex;
import com.wlyuan.index.domain.index.OrderIndexScroll;
import com.wlyuan.index.domain.index.builder.OrderIndexSumBuilder;
import com.wlyuan.index.domain.index.builder.OrderIndexSumsBuilder;
import com.wlyuan.index.infrastructure.config.OrderIndexProperties;
import com.wlyuan.index.infrastructure.utils.MergeUtils;
import com.wlyuan.tools.Page;
import com.wlyuan.tools.PageSort;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author yuanjie
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class OrderIndexSearchService extends AbstractSearchService {
    private final OrderIndexProperties indexProperties;
    private final RestHighLevelClient restHighLevelClient;

    @Override
    public String getIndexName() {
        return OrderIndex.class.getAnnotation(Document.class).indexName();
    }

    private List<OrderIndexFilterDTO> applyPre(String name, List<OrderIndexFilterDTO> filters) {
        var preFilter = indexProperties.getFilters().get(name);
        if (null == preFilter) {
            return filters;
        }

        return filters.stream()
                .map(filter -> MergeUtils.fill(filter, preFilter))
                .collect(Collectors.toList());
    }

    public Page<OrderIndexVO> search(String name) {
        var filter = this.indexProperties.getFilters().get(name);
        if (null == filter) {
            filter = new OrderIndexFilterDTO();
        }
        return this.search(filter);
    }

    public Page<OrderIndexVO> search(OrderIndexFilterDTO filter) {
        if (logger.isDebugEnabled()) {
            logger.info("订单搜索: {}", JSON.toJSONString(filter));
        }
        var request = createRequest(filter, indexProperties.getRules());
        try {
            var requestOptions = createRequestOptions(filter.getTenantId());
            SearchResponse response = restHighLevelClient.search(request, requestOptions);
            var page = new Page<OrderIndexVO>(filter.getPage(), filter.getSize());
            page.setRecords(createIndices(response.getHits(), OrderIndexVO.class));
            page.setTotal(response.getHits().getTotalHits().value);
            return page;
        } catch (Exception e) {
            logger.error("查询订单索引失败: {}", filter, e);
            throw new ValueError("查询订单索引失败");
        }
    }

    private Page<OrderIndexVO> search(List<OrderIndexFilterDTO> filters, PageSort page) {
        if (logger.isDebugEnabled()) {
            logger.info("订单搜索: {}", JSON.toJSONString(filters));
        }
        var request = createRequest(filters, indexProperties.getRules(), page);
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            var result = new Page<OrderIndexVO>(page.getPage(), page.getSize());
            result.setRecords(createIndices(response.getHits(), OrderIndexVO.class));
            result.setTotal(response.getHits().getTotalHits().value);
            return result;
        } catch (Exception e) {
            logger.error("查询订单索引失败: {} {}", filters, page, e);
            throw new ValueError("查询订单索引失败");
        }
    }

    public Page<OrderIndexVO> search(OrderIndexSearchDTO search) {
        if (CollectionUtils.isEmpty(search.getFilters())) {
            return this.search(search.getFilterName());
        }

        var filters = this.applyPre(search.getFilterName(), search.getFilters());
        return this.search(filters, search);
    }

    public OrderIndexScroll scroll(OrderIndexFilterDTO filter) {
        var request = createScrollRequest(filter, indexProperties.getRules());
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            var indices = createIndices(response.getHits(), OrderIndex.class);
            var total = response.getHits().getTotalHits().value;
            return new OrderIndexScroll(response.getScrollId(), total, indices);
        } catch (Exception e) {
            logger.error("查询订单索引失败: {}", filter, e);
            throw new ValueError("查询订单索引失败");
        }
    }

    public OrderIndexScroll scroll(String scrollId) {
        try {
            SearchScrollRequest request = createScrollRequest(scrollId);
            SearchResponse response = restHighLevelClient.scroll(request, RequestOptions.DEFAULT);
            var indices = createIndices(response.getHits(), OrderIndex.class);
            var total = response.getHits().getTotalHits().value;
            return new OrderIndexScroll(response.getScrollId(), total, indices);
        } catch (Exception e) {
            logger.error("查询订单索引失败: {}", scrollId, e);
            throw new ValueError("查询订单索引失败");
        }
    }


    public OrderIndexSumVO searchSum(OrderIndexFilterDTO filter) {
        var sourceBuilder = createBuilder(filter, indexProperties.getRules());
        var sumBuilder = new OrderIndexSumBuilder();
        sumBuilder.build(sourceBuilder);

        if (logger.isDebugEnabled()) {
            logger.debug("订单索引统计: {}", sourceBuilder);
        }

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(getIndexName());
        searchRequest.source(sourceBuilder);
        try {
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            return sumBuilder.collect(response.getAggregations());
        } catch (Exception e) {
            logger.error("订单索引统计失败: {}", sourceBuilder, e);
            throw new ValueError("订单索引统计失败");
        }
    }

    public Map<Long, OrderIndexSumVO> searchSums(OrderIndexFilterDTO filter) {
        var sourceBuilder = createBuilder(filter, indexProperties.getRules());
        var sumBuilder = new OrderIndexSumsBuilder("tenantId");
        sumBuilder.build(sourceBuilder);

        if (logger.isDebugEnabled()) {
            logger.debug("订单索引统计: {}", sourceBuilder);
        }

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(getIndexName());
        searchRequest.source(sourceBuilder);
        try {
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            return sumBuilder.collect(response.getAggregations());
        } catch (Exception e) {
            logger.error("订单索引统计失败: {}", sourceBuilder, e);
            throw new ValueError("订单索引统计失败");
        }
    }
}